package com.woniuxy.yoga.coach.service.permission;


import com.woniuxy.yoga.coach.client.AdminClient;
import com.woniuxy.yoga.coach.model.client.AdminRole;
import com.woniuxy.yoga.coach.model.client.Resource;
import com.woniuxy.yoga.coach.model.client.RoleResource;
import com.woniuxy.yoga.coach.model.exception.CoachException;
import com.woniuxy.yoga.commons.core.model.Result;
import com.woniuxy.yoga.commons.core.util.JWTUtil;
import com.woniuxy.yoga.commons.redis.util.RedisUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

//@Component
@Aspect
public class PermissionAop {
    @Autowired
    private AdminClient adminClient;
    @Autowired
    private RedisUtil redisUtil;
    @Pointcut("@annotation(com.woniuxy.yoga.coach.service.permission.Permission)")
    private void pt(){}

    //使用前置通知时，使用的是JoinPoint类型的对象，该对象直接去获取该方法的类的对象去newInstance产生类
    //我们需要将其改为环绕通知
    @Around("pt()")
    public Object permissionController(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("---------------进入管理员动态权限校验-------------------");
        //先校验管理员是否合法
        //从jwt令牌中获取当前登录管理员的账号id
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        String adminToken = request.getHeader("authen_token");
        String refreshToken = redisUtil.get(adminToken);
        //获取登录管理员的ID
        Integer accountId = JWTUtil.getAdminId(refreshToken);
        //判断令牌是否存在，如果存在，做下一步操作，如果不存在，没有登录，抛出异常
        if(!StringUtils.hasLength(refreshToken)) throw new CoachException(401,"登录未认证");

        //根据账号id查询当前管理员拥有的角色
        Result<AdminRole> adminRoleResult = adminClient.selectRoleByAccountId(accountId);
        if (adminRoleResult.getCode()!=200) {
            throw new CoachException(adminRoleResult.getCode(),adminRoleResult.getMessage());
        }
        AdminRole adminRole = adminRoleResult.getData();
        //查权限
        Result<List<RoleResource>> roleResourceResult = adminClient.selectResourceByRole(adminRole.getRoleId());
        if (roleResourceResult.getCode()!=200) {
            throw new CoachException(roleResourceResult.getCode(),roleResourceResult.getMessage());
        }
        List<RoleResource> roleResources = roleResourceResult.getData();
        //将资源权限ID存入集合中
        List<Integer> resourceIds = roleResources.stream().map(o -> o.getResourceId()).collect(Collectors.toList());
        //通过资源权限ID去查找资源集合
        List<Resource> resources = new ArrayList<>();
        if (resourceIds!=null&&resourceIds.size()>0) {
            Result<List<Resource>> resourceResult = adminClient.selectResourceByIds(resourceIds);
            if (resourceResult.getCode()!=200) {
                throw new CoachException(resourceResult.getCode(),resourceResult.getMessage());
            }
            resources = resourceResult.getData();
        }

        Method method = ((MethodSignature) pjp.getSignature()).getMethod();
        Object[] args = pjp.getArgs();
        //如果切入点方法上存在Permission注解，表示访问该方法需要进行权限控制
        boolean flag = false;
        Object value = null;
        if (method.isAnnotationPresent(Permission.class)) {
            Permission permission = method.getAnnotation(Permission.class);
            String[] resourceNames = permission.resources();
            //默认情况，只需认证，不需要授权
            if (resourceNames.length==0) {
                value = pjp.proceed(args);
            }else {
                //判断权限
                for (String resourceName : resourceNames) {
                    for (Resource resource : resources) {
                        if (resource.getResourcePath().equals(resourceName)) {
                            flag=true;
                            break;
                        }
                    }
                    if (flag) {
                        break;
                    }
                }

                if (flag) {
                    value = pjp.proceed(args);
                }else {
                    throw new CoachException(403,"没有权限");
                }
            }
        }
        return value;
    }

}
